iT邦幫忙

2022 iThome 鐵人賽

DAY 13
0
自我挑戰組

嘗試30天學「不」會Rust系列 第 13

[Rust] 常用集合 - 雜湊表(Hash Map)

  • 分享至 

  • xImage
  •  

環境

OS: Windows 10
Editor: Visual Studio Code
Rust version: 1.63.0

建立與加入

如同許多程式語言,都會有幾個內建的Key-value pair的集合型別,今天要來試試看Rust語言中的雜湊表(Hash map)。

首先是建立與加入新的資料,我們要先加入這個模組,在main.rs的最上面:

use std::collections::HashMap;

建立的方式有幾種,第一種,最平常的:

let mut scores = Hash::new();

scores.insert(String::from("RED"), 10);
scores.insert(String::from("BLUE"), 50);

可以看到上面的寫法沒有先定義scoreskey-value型別是甚麼,直到加入資料讓compiler自己判斷,上面定義的Hash Map型別是HashMap<String, i32>

再來我們可以用兩個同樣大小的向量(vector),蒐集成一個Hash Map:

let teams = vec![String::from("RED"), String::from("BLUE")];
let initial_scores = vec![10, 50];

let mut scores: HashMap<_, _> = teams.into_iter().zip(initial_scores.into_iter()).collect();

可以發現,這個做法需要提前告訴compilercollect()出來的型別是Hash Map,所以要加上HashMap<_, _>,當然也可以直接定義HashMap<String, i32>,使用HashMap<_, _>可以讓compiler自行判斷我們加入的型別是甚麼。

取值與遍歷

取值

如何取得Hash Map中的資料:

let blue_team_score = scores.get("BLUE");
match blue_team_score {
    Some(&score) => println!("BLUE team score is {}", score),
    None => println!("There is no BLUE team"),
};

跟向量(vector)一樣,回傳的是Option<T>,要開發者去處理,有值跟跟沒值的情況。

遍歷

// 元組(tuple)取得key跟value
for (key, val) in &scores {
    println!("Team {} got {}.", key, val);
}

更新

更新Hash map的時候,會發生一個一問題,Key是不是已經存在Hash Map裡面了。當然,我們可以再次使用insert,但這會把原本的值蓋掉:

scores.insert(String::from("BLUE"), 50);
scores.insert(String::from("BLUE"), 20); // 現在BLUE強制被改成20了

如果只是要單純檢查有沒有Key,可以使用contains_key

println!("Is red team exists > {}", scores.contains_key("RED"));

更新的話可以使用get_mut來更新資料:

match scores.get_mut("BLUE") {
    Some(score) => {
        *score = 30;
        println!("BLUE team score is {}", score);
    }
    None => {
        scores.insert(String::from("BLUE"), 30);
        println!("Add new team, BLUE");
    }
};

或是有一個更漂亮的寫法:

// BLUE隊直接設成30分
let blue_team_score = scores.entry(String::from("BLUE")).or_insert(0);
*blue_team_score = 30;

or_insert這個會把不存在Key加入一個新的值,但如果Key已經存在了,則不會把已經存在的值蓋過去。

然後在or_insert之前有一個entry,這會把Key-value pair整個回傳回來,我們可以試著這樣改寫,雖然看起來更複雜,但嘗試看看entry展開來的樣子:

use std::collections::{
    hash_map::Entry::{Occupied, Vacant},
    HashMap,
};

 // ....

let blue_team_score = match scores.entry(String::from("BLUE")) {
    Occupied(entry) => entry.into_mut(),
    Vacant(entry) => entry.insert(0),
};
*blue_team_score = 30;

刪除

標準函式庫有給兩種刪除的API,一樣會刪除(講廢話),但會傳的東西不同,請看:

// remove_entry
if let Some((team_name, score)) = scores.remove_entry("RED") {
    println!("Delete team: {}, latest scrore: {}.", team_name, score);
} else {
    println!("There is no team called RED.");
}
// remove
if let Some(score) = scores.remove("RED") {
    println!("Delete team RED, latest scrore: {}.", score);
} else {
    println!("There is no team called RED.");
}

看得出來,remove_entry會回傳整個Key-value pair然後用Option<T>包裝起來;remove會回傳value,一樣是被Option<T>包裝起來。

Reference


上一篇
[Rust] 常用集合-字串 (string)
下一篇
[Rust] 錯誤處理
系列文
嘗試30天學「不」會Rust18
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言